package com.boarsoft.boar.soagov.health;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.boarsoft.common.util.RandomUtil;
import com.boarsoft.common.util.StreamUtil;
import com.boarsoft.message.bean.Message;
import com.boarsoft.message.core.MessageService;
import com.boarsoft.soagov.bean.AlertMonitorData;
import com.boarsoft.soagov.bean.MonitorData;
import com.boarsoft.soagov.health.HealthChecker;

/**
 * 检查HTTP服务是否健康
 * 
 * @author Mac_J
 *
 */
public class HttpCheckRunner implements HealthChecker, Runnable {
	private static final Logger log = LoggerFactory.getLogger(HttpCheckRunner.class);

	/** 以下是XML注入 */
	protected String url;
	protected int connectTimeout = 3000;
	protected int readTimeout = 3000;
	protected String contentType = "application/json";
	protected String body = "";
	protected String encIn = "UTF-8";
	protected String result;
	protected int responseCode = 200;
	protected String cookie = "";
	protected Map<String, String> headers = new HashMap<String, String>();
	protected int interval = 30;
	protected int retry = 3;
	protected MessageService messageSvc;
	protected String appCode;

	/** 运行时属性 */
	protected String addr;
	protected String path;
	protected boolean status = true;
	protected Future<?> future;
	protected int tried;

	@Override
	public void run() {
		if (this.check(addr)) {
			return;
		}
		tried++;
		if (tried < retry) {
			log.warn("{} can not be access, retry in {}s", addr, interval);
			return;
		}
		status = false;
		log.warn("{} is down after retried {}", addr, tried);
		tried = 0;
		// 送通知消息
		if (messageSvc == null) {
			return;
		}
		MonitorData d = new AlertMonitorData();
		d.setId(RandomUtil.randomUUID());
		d.setKey(appCode.concat(".down"));
		d.setAddr(addr);
		Message m = new Message();
		// m.setFrom("");
		m.setTitle(new StringBuilder()//
				.append(addr).append(" is down").toString());
		m.setContent(d); // TODO
		m.setTarget("tq_monitor_event");
		// m.setTag("down");
		try {
			messageSvc.put(m);
		} catch (Exception e) {
			log.error("Error on send alert {}", m, e);
		}
	}

	@Override
	public boolean check(String addr) {
		String url = String.format(this.url, addr);
		log.info("Check HTTP health with url {}", url);
		PrintWriter out = null;
		BufferedReader br = null;
		HttpURLConnection conn = null;
		StringBuilder sb = new StringBuilder();
		try {
			conn = (HttpURLConnection) new URL(url).openConnection();
			conn.setConnectTimeout(connectTimeout);
			conn.setReadTimeout(readTimeout);
			conn.setRequestProperty("User-Agent", "Mozilla/5.0");
			conn.setRequestProperty("Content-Type", contentType);
			conn.setDoOutput(true);
			conn.setDoInput(true);
			// 设置cookie
			if (cookie != null) {
				conn.setRequestProperty("Cookie", cookie);
			}
			// headers
			if (headers != null && !headers.isEmpty()) {
				for (String h : headers.keySet())
					conn.setRequestProperty(h, headers.get(h));
			}
			// body
			out = new PrintWriter(conn.getOutputStream());
			out.print(body);
			out.flush();
			br = new BufferedReader(new InputStreamReader(conn.getInputStream(), encIn));
			String line = null;
			while ((line = br.readLine()) != null) {
				sb.append(line).append("\n");
			}
			log.debug("Response from URL {} is {} = {}", url, //
					conn.getResponseCode(), conn.getResponseMessage());
			// 检查响应结果和状态码
			if (conn.getResponseCode() == responseCode) {
				if (result == null) {
					return true;
				}
			} else {
				return false;
			}
		} catch (IOException e) {
			log.error("Error on test url {}, {}", url, e.getMessage());
			return false;
		} finally {
			StreamUtil.close(br);
			StreamUtil.close(out);
			if (conn != null) {
				conn.disconnect();
			}
		}
		String rs = sb.toString();
		if (result.equals(rs)) {
			return true;
		}
		if (rs.indexOf(result) >= 0) {
			return true;
		}
		if (rs.matches(result)) {
			return true;
		}
		return false;
	}

	public void stop() {
		if (future == null || future.isCancelled()) {
			return;
		}
		future.cancel(true);
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public int getConnectTimeout() {
		return connectTimeout;
	}

	public void setConnectTimeout(int connectTimeout) {
		this.connectTimeout = connectTimeout;
	}

	public int getReadTimeout() {
		return readTimeout;
	}

	public void setReadTimeout(int readTimeout) {
		this.readTimeout = readTimeout;
	}

	public String getContentType() {
		return contentType;
	}

	public void setContentType(String contentType) {
		this.contentType = contentType;
	}

	public String getBody() {
		return body;
	}

	public void setBody(String body) {
		this.body = body;
	}

	public String getEncIn() {
		return encIn;
	}

	public void setEncIn(String encIn) {
		this.encIn = encIn;
	}

	public String getResult() {
		return result;
	}

	public void setResult(String result) {
		this.result = result;
	}

	public String getCookie() {
		return cookie;
	}

	public void setCookie(String cookie) {
		this.cookie = cookie;
	}

	public Map<String, String> getHeaders() {
		return headers;
	}

	public void setHeaders(Map<String, String> headers) {
		this.headers = headers;
	}

	public String getAddr() {
		return addr;
	}

	public void setAddr(String addr) {
		this.addr = addr;
	}

	public String getPath() {
		return path;
	}

	public void setPath(String path) {
		this.path = path;
	}

	public int getInterval() {
		return interval;
	}

	public void setInterval(int interval) {
		this.interval = interval;
	}

	public int getRetry() {
		return retry;
	}

	public void setRetry(int retry) {
		this.retry = retry;
	}

	public boolean getStatus() {
		return status;
	}

	public void setStatus(boolean status) {
		this.status = status;
	}

	public Future<?> getFuture() {
		return future;
	}

	public void setFuture(Future<?> future) {
		this.future = future;
	}

	public int getResponseCode() {
		return responseCode;
	}

	public void setResponseCode(int responseCode) {
		this.responseCode = responseCode;
	}

	public MessageService getMessageSvc() {
		return messageSvc;
	}

	public void setMessageSvc(MessageService messageSvc) {
		this.messageSvc = messageSvc;
	}

	public String getAppCode() {
		return appCode;
	}

	public void setAppCode(String appCode) {
		this.appCode = appCode;
	}
}